﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Compiler.TableOfSymbols.Types;
using Compiler.Unification.Substitutions;

namespace Compiler.Unification.Constraints
{
    /// <summary>
    /// Базовый класс ограничения
    /// </summary>
    public abstract class BaseTypeConstraint : IComparable<BaseTypeConstraint>
    {
        private BaseType leftTypeArg;
        private BaseType rightTypeArg;
        private int viewCount;

        public BaseType LeftTypeArgument
        { 
            get { return leftTypeArg; } 
        }
        public BaseType RightTypeArgument
        { 
            get { return rightTypeArg; } 
        }

        public int ViewCount
        { 
            get { return viewCount; } 
        }

        protected abstract int CategoryWeight 
        { 
            get; 
        }

        protected int weight
        { 
            get { return viewCount * 100 + CategoryWeight; } 
        }

        public abstract bool IsSameType 
        { 
            get; 
        }
        public abstract bool IsSubType 
        { 
            get; 
        }

        public BaseTypeConstraint(BaseType leftTypeArg, BaseType rightTypeArg)
            : this(leftTypeArg, rightTypeArg, 0)
        { }
        public BaseTypeConstraint(BaseType leftTypeArg, BaseType rightTypeArg, int viewCount)
        {
            if (leftTypeArg == null)
                throw new ArgumentNullException("leftTypeArg can not be null");
            if (rightTypeArg == null)
                throw new ArgumentNullException("rightTypeArg can not be null");
            if (viewCount < 0)
                throw new ArgumentOutOfRangeException("viewCount can not be negative");
            this.leftTypeArg = leftTypeArg;
            this.rightTypeArg = rightTypeArg;
            this.viewCount = viewCount;
        }

        public void IncViewCount()
        {
            ++viewCount;
        }

        public override int GetHashCode()
        {
            return leftTypeArg.GetHashCode() * 701
                + rightTypeArg.GetHashCode() * 3037;
        }

        public int CompareTo(BaseTypeConstraint other)
        {
            return this.weight.CompareTo(other.weight);
        }

        public abstract BaseTypeConstraint MakeNewConstraint(BaseType leftTypeArg, BaseType rightTypeArg);

        public abstract string OperatorString
        {
            get;
        }

        public string MakeDescription(TableOfSymbols.NETTypesBijectiveMap typesMap)
        {
            string left = (this.leftTypeArg.IsTypeVariable) ? leftTypeArg.ToString()
                : typesMap.GetFullDescription(this.leftTypeArg);
            string right = (this.rightTypeArg.IsTypeVariable) ? rightTypeArg.ToString()
                : typesMap.GetFullDescription(this.rightTypeArg);
            return String.Format("{0}{2}{1}", left, rightTypeArg, OperatorString);
        }
    }
}
